package com.hongpu.config;

import com.alibaba.fastjson2.JSONObject;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.hongpu.utils.JwtUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.exceptions.TooManyResultsException;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingResponseWrapper;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


/**
 * @author 张玉胜
 * @version 1.0
 * @date 2023/10/11 15:08
 * @description
 */
@Slf4j
@Component
public class TokenAuthenticationFilter extends OncePerRequestFilter {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    private static final String AUTHORITY_CLAIM_NAME = "Authorization";
    private static final String BEARER_TOKEN_STARTS_WITH = "Bearer ";
    // 忽略验证token的URI
    private static final String[] IGNORE_URI = {"/auth","/druid"};

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        // 使用包装响应来防止过早提交
        ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(httpResponse);

     //   log.info("**********进入过滤器:TokenAuthenticationFilter.doFilterInternal**********");
        try {
            String requestURI = request.getRequestURI();
      //      log.info("**********请求的URI:requestURI:{}**********", requestURI);
            boolean match = Arrays.stream(IGNORE_URI).anyMatch(s -> requestURI.contains(s));
            if (match) {
                filterChain.doFilter(request, response);
            } else {
                String token = extractToken(request);
//                log.info("**********请求的Authorization:{}**********", token);
                if (token != null && validateToken(token)) {
                    Authentication authentication = getAuthentication(token);
                    if (authentication != null) {
                        SecurityContextHolder.getContext().setAuthentication(authentication);
                    }
                } else {
                    response.setStatus(HttpStatus.UNAUTHORIZED.value());
                    response.setContentType("application/json");
                    response.setCharacterEncoding("UTF-8");
                    JSONObject errorMsg = new JSONObject();
                    errorMsg.put("code", HttpStatus.UNAUTHORIZED.value());
                    errorMsg.put("message", "系统检测到无效令牌,请先登录账号");
                    response.getWriter().write(errorMsg.toString());
                    return;
                }
                filterChain.doFilter(request, wrappedResponse);
                wrappedResponse.copyBodyToResponse();
            }
        }catch (ServletException e) {
            if (!wrappedResponse.isCommitted()) {
                wrappedResponse.reset();
                wrappedResponse.setStatus(HttpStatus.BAD_REQUEST.value());
                wrappedResponse.setContentType("application/json");
                wrappedResponse.setCharacterEncoding("UTF-8");
                JSONObject errorMsg = new JSONObject();
                errorMsg.put("code", HttpStatus.BAD_REQUEST.value());
                String exceptinMsg = e.getLocalizedMessage();
                String message = exceptinMsg.substring(exceptinMsg.lastIndexOf(":") + 1);
                errorMsg.put("message", message);
                wrappedResponse.getWriter().write(errorMsg.toString());
                wrappedResponse.flushBuffer();
            }
        }
    }

    private String extractToken(HttpServletRequest request) {
        String bearerToken = request.getHeader(AUTHORITY_CLAIM_NAME);
        if (bearerToken != null) {
            if (bearerToken.startsWith(BEARER_TOKEN_STARTS_WITH)) {
                return bearerToken.substring(7);
            } else {
                return bearerToken;
            }
        } else {
            return null;
        }
    }


    private boolean validateToken(String token) {
        Boolean hasKey = stringRedisTemplate.hasKey(token);
     //   log.info("**********Redis中存在token:{}**********", hasKey);
        return hasKey;
    }

    private Authentication getAuthentication(String token) {
        DecodedJWT decodedJWT = JwtUtils.verify(token);
        String userAccount = decodedJWT.getIssuer();
        // 权限集合
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        return new UsernamePasswordAuthenticationToken(userAccount, null, authorities);
    }
}
